// TypeScript
//		从JS中调用TS,TS编译出JS,运行在brower,NodeJs环境，和支持JS的引擎
//		TS允许开发时，使用高效的开发工具和常用操作（包括静态类型检查，和代码重构）
//		TS提供的新特性，帮助建立强壮的组件，比如：
//			接口和类，可以共同作用，自行决定抽象级别。



//类型注解
// function greeter(person:string) {
//     return "Hello, " + person;
// }
// 
// let user = "Jane User";
// 
// document.body.innerHTML = greeter(user);

//类
// class Student {
// 	fullName:string;
// 	//在构造函数上使用public等同于创建了同名的成员变量
// 	constructor(public firstName, public middleInitial, public lastName)
// }

//接口
// interface Person{
// 	firstName:string;
// 	lastName:string;
// }
// function greeter(person:Person){
// 	return "hello,"+person.firstName+" "+person.lastName;
// }
// let user = {firstName:'Jane',lastName:'User'};

//元组(tuple)类型 允许表示一个已知元素数量和类型的数组，各元素的类型不必相同
let x:[string,number]
x = ['hello',58]


//枚举类型 使用枚举数据类型可以为一组数据赋予友好的名字,so,便利之处就是由枚举的值得到他映射的名字
enum Color{Red,Green,Blue}
let c:Color = Color.Green
let colorName:string = Color[2]
console.log(c,colorName)

//any 为在编程阶段还不清楚类型的变量制定一个类型,在对代码进行改写时，允许你在编译时可选择的包含或移除类型检查
//	  并不能使用Object类型代替他的作用
let notSure:any = 4;
notSure = '我是字符串'
notSure = true;

//void //与any类型相反，表示没有任何类型，只能赋予undefined或null,用法：
function warnUser():void{
		console.log('...')//函数没有返回值，指定其返回值类型为Void
}
console.log(warnUser())

//undefined,null,默认情况是所有类型的子类型，尽可能使用 -- strictNullChecks标记，这样，null,undefined只能赋予本身和void

//nerver 是那些永远不存在的值得类型，，常用在总是抛出异常或根本不会有返回值的函数表达式或箭头函数的返回值类型
function error(message:string):never{
	throw new Error(message)
}
function infiniteLoop():never{
	while(true){
		
	}
}

//类型断言
//通过类型断言这种方式告诉编译器，能做到比现有类型，更精确的类型，
//像是其他语言的类型转换，但是不进行特殊的数据检查和解构，在编译阶段起作用
//尖括号表示法
let someValue:any = "this is a string";
let strLength:number = (<string>someValue).length;
//as语法
// let someString:any = 'this is a string';
// let strLength:number = (someString as string).length//使用JSX时，只有as语法被允许



//基础类型有 布尔值(boolean) 数字(number) 字符串(string) null undefined  any void never 数组 元组(tuple) 枚举(enum) object
//数组有两种表示方法①  元素类型[]  ② Array<元素类型>  数组泛型
//元组允许表示一个已知元素数量和元素类型的数组 [string,number] 当访问越界元素时，会使用联合类型代替
//枚举数据类型是对js标准数据类型的补充，可以为一组数值赋予友好的名字，并由枚举的值得到他的名字
//any允许编译时可选择的包含或移除类型检查，并在它上面调用本身有的方法
//object表示非原始类型之外的类型



//interfaces(接口)就是：在做duck typing(鸭式辩型)或structural subtyping(结构化子类型),也就是对值所具有的结构进行类型检查时,对类型命名并对代码或第三方代码定义契约.
//interfaces就好比是一个名字，类型检查器只会关注值的外形。不会检查属性的顺序。
//interfaces里的可选属性(?)，对可能存在的属性进行预定义
//interfaces里的只读属性(readonly)，这种属性只允许在对象刚刚创建的额时候修改值，
//interfaces额外的属性检查：将对象字面量赋值给变量或作为参数传递时，如果对象字面量存在任何目标类型不包含的属性时，会得到错误
//为interfaces添加字符串索引签名([propName: string]: any)，前提是能够确定这个对象可能具有某些做为特殊用途使用的额外属性,用来解决上面的问题
//interfaces能够描述函数类型，（params1；类型，params2；类型）： 类型  ，对于函数类型，函数参数的检查只要求对应位置的参数类型是兼容的。
//interfasces能够描述通过索引得到的类型，分为两种①字符串索引([propName: string]: any)，②数字索引([index: number]: any)\
//interfaces还能强制使类符合接口的契约，可以在接口中描述一个方法，在类里实现，接口描述了类的公共部分，而不是公共和私有两部分，他不会帮你检查类是否具有某些私有成员
interface ClockInterface {
	currentTime: Date;
	setTime(d: Date);
}
class Clock implements ClockInterface {
	currentTime: Date;
	setTime(d: Date){
		this.currentTime = d;
	};
	constructor(h: number, m: number){}
}
//接口之间可以继承，一个接口可以继承多个接口，创建出多个接口的合成接口。
//接口里可以定义丰富的混合类型,在使用第三方库的时候，可以定义完整的类型
interface Counter {
	(start: number): string;
	interval: number;
	reset(): void;
}
function getCounter(): Counter{
	let counter = <Counter>function(start: number){ };
	counter.interval = 123;
	counter.reset = function(){};
	return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
//接口可以继承类

//Function Types

// 普通写法
function add(x,y){
	return x + y
}
let myAdd = function(x,y){return x + y}
//为函数定义类型
function add(x: numnber,y: number): number{
	return x + y
}
let myAdd = function(x:number,y:number): number{ return x + y }
//为函数定义完整类型
let myAdd: (x: number,y: number) => number = function(x: number,y: number): number{ return x + y }
//函数类型包含两部分：参数类型和返回值类型。我们可以在定义函数类型时，以参数列表的形式定义参数类型，增加可读性，只要参数类型是匹配的就是有效的函数类型
let myAdd: (baseValue: number,increment: number) => number = function(x: number,y: number): number{ return x + y }
//函数类型可以做类型推论


//Optional Parameters(可选参数)与 Default Parameters(默认参数)

//与js函数不同的是，传递给一个函数的参数个数与参数的期望值必须一致，多传或少传都不行
// 要实行可选参数的功能有两种方法
// ①param?   使用可选参数，可选参数必须跟在必传参数后面，
//②使用默认初始化值的参数，就是为参数提供一个默认值当用户没有传递这个参数或传的参数是undefined,与可选参数不同的是，不用必须带在可选参数后面
//相同之处是，可选参数与末尾默认参数共享函数的参数类型
function buildName(firstName: string, lastName?: string) {
    // ...
}
function buildName(firstName: string, lastName = "Smith") {
    // ...
}  //共享同样的函数类型，(firstName:string,lastName?string) => string

//Rest Parameters
//与js函数中arguments作用类似的是可以使用剩余参数把所有参数收集到一个变量里
function buildName(firstName: string,...restOfName:string[]){
	return firstName + ' ' + restOfName.join(' ');
}

//THIS

//在没有箭头函数之前，在返回函数或将函数当做参数传递的时候，弄清楚函数调用的上下文（即弄清this的指向）不是简单的事
//然而，es6中箭头函数中this能保存函数创建时的值，而不是调用时的值
//this参数
// 语法: function f(this: void){}
interface Card {
	suit: string;
	card: string;
}
interface Deck {
	suits: string[];
	cards: string[];
	createCardPicker: (this: Deck): () => Card;
}
let deck: Deck = {
	suits: ['hearts','spades','clubs','diamonds'],
	cards:Array(52),
	createCardPicker:function(this: Deck){
		return () => {
			let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);
            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
		}
	}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);
//createCardPicker期望在某个Deck对象上调用，也就是说this是Deck类型的，
//回调函数里的this，函数重载


//Generics(泛型)：是程序设计语言的一种特性，允许在强类型设计语言中编写时定义一些可变部分，可变部分必须在使用前做出说明。
//参数类型是一个变量
//由于这种特性，使得编写的代码（组件或其他）不仅能够支持当前的数据类型，也能够支持未来的数据类型。
function identity(arg: number): number{
	return arg
}//有局限性
function identity(arg: any):any{
	return arg
}//会丢失信息，加入传入一个数字，将会返回任何类型的值
//Hello Word! 通过使用类型变量，创建一个使用generics的例子 identity函数,使返回值类型与传入的参数类型相同
function identity<T>(arg: T): T {
	return arg
}//把泛型变量，当做参数类型
//这个泛型函数适用多种类型，不会丢失信息，并保持传入类型与返回的类型的一致
//泛型函数的使用方法有两种
//①
let output = identity<string>('mystring')//明确指出T是string类型，作为一个参数传入函数
//②
let output = identity('mystring')//利用类型推论，编译器根据传入的参数自动确定传入的类型

//使用泛型变量
function loggingIdentity<T>(arg: T[]): T[]{
	return arg
}
//这个函数接收元素类型时T的数组，这样就把泛型变量当做类型的一部分使用，而不是整个类型，增加了灵活性
//泛型函数的类型，只要在最前面加一个类型参数
function identity<T>(arg: T): T{
	return arg
}
let myIdentity: <T>(arg: T) => T = identity
//使用带有调用签名的对象字面量定义泛型函数,这样就可以写出泛型接口了。
let myIdentity: {<T>(arg: T):T} = identity

//创建一个泛型接口
interface GenericIdentityFn {
	<T>(arg: T): T;
}
function identity<T>(arg: T): T{
	return arg
}
let myIdentity : GenericIdentityFn = identity;
//把泛型接口当做整个接口的参数，可以清楚的知道具体使用的是哪个泛型类型
interface GenericIdentityFn<T>{
	(arg:T): T;
}
let myIdentity: GenericIdentityFn<number> = identity;//把非泛型函数签名作为泛型类型的一部分，当使用GenericIdentityFn的时候，传入一个类型参数来指定泛型类型。


//classes(类)  reuseable components(可重用组件)
class Greeter {//在引用类成员的时候使用this,this表示我们访问的是类成员
	greeting: string;
	constructor(message: string){
		this.greeting = message
	}
	greet(){
		return "hello, " + this.greeting;
	}
}
let greeter = new Greeter('world');//实例化的过程是：创建一个Greeter类型的新对象，并执行构造函数初始化它。

//derived calsses(subClasses,派生类)会从base(superclasses,基类) inheirt 属性和方法。
//subclasses可以包含一个构造函数，一定要在subclasses的构造函数，访问this之前，执行super(),它会执行superclasses的构造函数。
//subclasses可以重写superclasses的方法。

//比较两种不同的类型
//structural type system(解构话类型系统)的特性：比较两种不同的types,忽略他们的出处，如果所有成员的types是compatible（兼容的），就认为这两种types是compatible
//但是当比较带有private和protected成员的类型，就要比较是否来自同一处声明。

//private 标记的成员不能在声明它的类的外部访问
//protected 标记的成员仅能在subclasses中访问。特殊的，被protected标记的constructor可以被继承，不能被访问。
//readonly 将属性设置为只读，且必须在声明时或者构造函数中被初始化。

//构造函数的参数属性：通过给构造函数前面添加一个标记（public,private,protected,readonly）会初始化一个class的成员

//存取器特性：在class中使用get和set实现控制对成员的访问

//静态属性（static）：类的静态成员存在于类本身,而不是类的实例上，访问时要加上类名。

//抽象类，作为其他派生类的基类使用

//在声明类的时候，首先声明了实例的类型，其次，我们创建了构造函数的值，这个函数会在我们使用 new创建类实例的时候被调用，
//类不仅可以实例化，还可以继续当做构造函数使用。我们可以创建变量，保存而不是执行类构造函数
//既然类可以创造实例类型，就可以在使用接口的地方使用类。

















